package com.qianxx.viewlibrary.effects.scrollpanel;

import android.content.Context;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.Scroller;

import com.qianxx.viewlibrary.recyclerview.refreshview.DisplayUtil;

/**
 * 滚动控件
 */
public class ScrollPanel extends LinearLayout implements View.OnTouchListener {
    //自定义滚动监听器
    OnScrollListener listener;
    //上一次触摸的横坐标
    public int lastX;
    //上一次触摸的纵坐标
    public int lastY;
    //水平偏移量
    public int dx;
    //垂直偏移量
    public int dy;
    //顶部悬停的纵坐标
    int topY;
    //中间悬停的纵坐标
    int middleY;
    //底部悬停的纵坐标
    int bottomY;
    //滑动回弹的阈值
    int springback;
    //滚动控件
    Scroller scroller;
    //向上
    public final static int UP = 0;
    //向下
    public final static int DOWN = 1;
    //移动方向
    int direction = DOWN;

    //是否悬停在头部
    boolean isTop;
    //是否悬停在中间
    boolean isMiddel;
    //是否悬停在底部
    boolean isBottom;
    //该字段用来避免onMeasure多次加载
    boolean isCan = true;

    OnInterceptTouchListener onInterceptTouchListener;

    public ScrollPanel(Context context) {
        this(context, null);
    }

    public ScrollPanel(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ScrollPanel(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setOnTouchListener(this);
        scroller = new Scroller(context);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        if (isCan) {
            setTopY(DisplayUtil.dp2px(getContext(), 90));
            setMiddleY(getMeasuredHeight() / 2);
            setBottomY(getMeasuredHeight() - DisplayUtil.dp2px(getContext(), 60));
            setSpringback(getMeasuredHeight() / 12);
            smoothScrollTo(0, -getMiddleY());
        }
    }

    /**
     * 调用此方法滚动到目标位置(平滑移动)
     *
     * @param fx
     * @param fy
     */
    public void smoothScrollTo(int fx, int fy) {
        int dx = fx - scroller.getFinalX();
        int dy = fy - scroller.getFinalY();
        smoothScrollBy(dx, dy);
    }

    /**
     * 调用此方法滚动到目标位置
     *
     * @param fx
     * @param fy
     */
    public void scrollTo(int fx, int fy) {
        int dx = fx - scroller.getFinalX();
        int dy = fy - scroller.getFinalY();
        scrollBy(dx, dy);
    }

    /**
     * 调用此方法设置滚动的相对偏移(平滑滚动)
     *
     * @param dx
     * @param dy
     */
    public void smoothScrollBy(int dx, int dy) {
        //设置mScroller的滚动偏移量，这里并非真实执行移动
        scroller.startScroll(scroller.getFinalX(), scroller.getFinalY(), dx, dy);
        invalidate();//这里必须调用invalidate()才能保证computeScroll()会被调用，否则不一定会刷新界面，看不到滚动效果
    }

    /**
     * 调用此方法设置滚动的相对偏移
     *
     * @param dx
     * @param dy
     */
    public void scrollBy(int dx, int dy) {
        scroller.startScroll(scroller.getFinalX(), scroller.getFinalY(), dx, dy, 0);
        invalidate();
    }

    @Override
    public void computeScroll() {
        //先判断mScroller滚动是否完成
        if (scroller.computeScrollOffset()) {
            //这里调用View的scrollTo()完成实际的滚动
            ((View) getParent()).scrollTo(scroller.getCurrX(), scroller.getCurrY());
            //必须调用该方法，否则不一定能看到滚动效果
            postInvalidate();

            setTop(-scroller.getFinalY() == getTopY());
            setMiddel(-scroller.getFinalY() == getMiddleY());
            setBottom(-scroller.getFinalY() == getBottomY());

            //监听滚动距离
            if (listener != null)
                listener.onScrollTo(-scroller.getFinalX(), -scroller.getFinalY());
        }
        super.computeScroll();
    }

    /**
     * 手势拦截事件
     *
     * @param ev
     * @return
     */
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        int x = (int) ev.getX();
        int y = (int) ev.getY();
        if (ev.getAction() == MotionEvent.ACTION_DOWN
                || ev.getAction() == MotionEvent.ACTION_UP) {//点击手势
            lastX = x;
            lastY = y;
            return false;
        } else if (ev.getAction() == MotionEvent.ACTION_MOVE
                && -scroller.getFinalY() <= getTopY() && dy > 0) {//上滑到顶部
            //得到偏移量
            dx = x - lastX;
            dy = y - lastY;
            //将滚动到顶部后是否进行事件拦截的处理通过监听器外放给外部处理
            if (onInterceptTouchListener != null)
                return onInterceptTouchListener.onScrollToTop();
            return true;
        } else if (ev.getAction() == MotionEvent.ACTION_MOVE
                && -scroller.getFinalY() > getTopY()) {//滑动中（还没滑到顶部）
            //得到偏移量
            dx = x - lastX;
            dy = y - lastY;
            return true;
        } else if (ev.getAction() == MotionEvent.ACTION_MOVE
                && -scroller.getFinalY() <= getTopY()) {//上滑动顶部
            //得到偏移量
            dx = x - lastX;
            dy = y - lastY;
            return false;
        }
        return true;
    }

    /**
     * 手势事件
     *
     * @param view
     * @param motionEvent
     * @return
     */
    @Override
    public boolean onTouch(View view, MotionEvent motionEvent) {
        isCan = false;
        int x = (int) motionEvent.getX();
        int y = (int) motionEvent.getY();
        switch (motionEvent.getAction()) {
            case MotionEvent.ACTION_DOWN:
                lastX = x;
                lastY = y;
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                if (-scroller.getFinalY() < getTopY()) {//超出上方可视范围
                    //自动滑动到顶部悬停的地方
                    scrollTo(0, -getTopY());
                } else if (-scroller.getFinalY() > getBottomY()) {//超出下方可视范围
                    scrollTo(0, -getBottomY());
                } else if (direction == DOWN && -scroller.getFinalY() - getHeight() / 2 < getSpringback()) {//下滑位移量很小的时候
                    //自动滑动到中间悬停的地方
                    smoothScrollTo(0, -getMiddleY());
                } else if (direction == DOWN && -scroller.getFinalY() > getHeight() / 2) {//下滑到一半以下时
                    //自动滑动到底部悬停的地方
                    smoothScrollTo(0, -getBottomY());
                } else if (direction == UP && -scroller.getFinalY() > getHeight() / 2) {//上滑到一半以上时
                    //自动滑动到中间悬停的地方
                    smoothScrollTo(0, -getMiddleY());
                } else if (direction == UP && scroller.getFinalY() + getHeight() / 2 > getSpringback()) {//下滑位移量很小的时候
                    //自动滑动到中间悬停的地方
                    smoothScrollTo(0, -getTopY());
                }
                break;
            case MotionEvent.ACTION_MOVE:
                //得到偏移量
                dx = x - lastX;
                dy = y - lastY;
                //设置移动方向
                direction = dy > 0 ? DOWN : UP;
                //移动
                if (-scroller.getFinalY() >= getTopY()
                        && -scroller.getFinalY() <= getBottomY())
                    scrollBy(0, -dy);
                break;
        }
        return true;
    }

    public interface OnScrollListener {
        /**
         * 监听滑动位置
         *
         * @param x
         * @param y
         */
        void onScrollTo(int x, int y);
    }

    public interface OnInterceptTouchListener {
        /**
         * 正在从中间滑动到顶部
         *
         * @return
         */
        boolean onScrollToTop();
    }

    public void setOnInterceptTouchListener(OnInterceptTouchListener onInterceptTouchListener) {
        this.onInterceptTouchListener = onInterceptTouchListener;
    }

    public void setOnScrollListener(OnScrollListener listener) {
        this.listener = listener;
    }

    public int getTopY() {
        return topY;
    }

    public void setTopY(int topY) {
        this.topY = topY;
    }

    public int getMiddleY() {
        return middleY;
    }

    public void setMiddleY(int middleY) {
        this.middleY = middleY;
    }

    public int getBottomY() {
        return bottomY;
    }

    public void setBottomY(int bottomY) {
        this.bottomY = bottomY;
    }

    public int getSpringback() {
        return springback;
    }

    public void setSpringback(int springback) {
        this.springback = springback;
    }

    public boolean isTop() {
        return isTop;
    }

    public void setTop(boolean top) {
        isTop = top;
    }

    public boolean isMiddel() {
        return isMiddel;
    }

    public void setMiddel(boolean middel) {
        isMiddel = middel;
    }

    public boolean isBottom() {
        return isBottom;
    }

    public void setBottom(boolean bottom) {
        isBottom = bottom;
    }

    public int getDirection() {
        return direction;
    }

    public void setDirection(int direction) {
        this.direction = direction;
    }

    public Scroller getScroller() {
        return scroller;
    }

    public void setScroller(Scroller mScroller) {
        this.scroller = mScroller;
    }
}
